package com.skillworld.webapp.model.gameservice;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.skillworld.webapp.model.bag.Bag;
import com.skillworld.webapp.model.bag.BagDao;
import com.skillworld.webapp.model.bag.ItemRecord;
import com.skillworld.webapp.model.department.Department;
import com.skillworld.webapp.model.department.rank.DeptRank;
import com.skillworld.webapp.model.gamemap.GameMap;
import com.skillworld.webapp.model.gamemap.GameMapDao;
import com.skillworld.webapp.model.gamemap.Position;
import com.skillworld.webapp.model.item.Item;
import com.skillworld.webapp.model.item.ItemDao;
import com.skillworld.webapp.model.itemservice.BagFullException;
import com.skillworld.webapp.model.itemservice.ItemService;
import com.skillworld.webapp.model.itemservice.NoLevelException;
import com.skillworld.webapp.model.itemservice.NoUsagesLeftException;
import com.skillworld.webapp.model.user.User;
import com.skillworld.webapp.model.user.UserDao;
import com.skillworld.webapp.model.util.PermissionDeniedException;

import es.udc.pojo.modelutil.exceptions.InstanceNotFoundException;

@Service("GameService")
@Transactional
public class GameServiceImpl implements GameService
{
    private final Random random = new Random();
    
	@Autowired
	private UserDao userDao;
	  
	@Autowired
	private GameMapDao gameMapDao;
	  
	@Autowired
    private ItemDao itemDao;


    public GameMap findMapByName(String mapName) 
            throws InstanceNotFoundException
    {
        return gameMapDao.findByName(mapName);
    }
	
	/*
	 * TO-DO: For now, it ignores GameMap.lockLevel property.
	 *        That stuff is not well-defined and only makes this more
	 *        complicated.
	 * TO-DO: For now, it only considers the first required item,
	 *        others are ignored.
	 */
	public GameMap joinMap(Long userId, String mapName) 
	        throws InstanceNotFoundException, PermissionDeniedException
	{
	    User user = userDao.findById(userId);
	    GameMap gameMap = gameMapDao.findByName(mapName);
	    Set<Item> reqItems = gameMap.getRequiredItems();
	    
	    if(!reqItems.isEmpty())
	    {
	       Item reqItem = reqItems.iterator().next();  // FIX LATER, see TO-DO notes
	       if (!user.getBag().hasItem(reqItem))
	       { throw new PermissionDeniedException(); }
	        
	       user.getBag().consumeItem(reqItem);
	    }
	    
	    user.joinMap(gameMap);
	    
	    return gameMap;
	}
    
    public void savePosition(Long userId, String mapName, Position pos)
            throws InstanceNotFoundException
    {
        User user = userDao.findById(userId);
        GameMap gameMap = gameMapDao.findByName(mapName);
        
        /*
         *  WORKARROUND: Ignore invalid requests
         *  This avoids the "stuck in the Master Coach area" problem.
         */
        if (gameMap != user.getCurrentMap())
        { return; }
        
        user.setLastPosition(pos);
    }
	
    public Item playCasino(long userId) 
            throws InstanceNotFoundException, 
                    PermissionDeniedException, 
                    BagFullException
    {
        User user = userDao.findById(userId);
        Item casinoItem = itemDao.getCasinoItem();

        if (!user.getBag().hasItem(casinoItem))
        { throw new PermissionDeniedException(); }
        
        if (user.getBag().isFull())
        { throw new BagFullException(userId); }

        user.getBag().consumeItem(casinoItem);
        
        if(random.nextInt(5) == 0)  // Probability of 20% to win a new item 
        {
            List<Item> allItems = itemDao.findAll();
            Item winned = allItems.get(random.nextInt(allItems.size()-1));
            user.getBag().addItem(winned);
            return winned;
        }
        else
        { return null; }
    }
    
}
